package com.ls.utils;

import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

/**
 * md5加密/SHA1加密
 * @author zhouyang
 * @date 2017年12月7日
 * pengyu 这里改过decode实现
 */
public class MD5Util {

    private static String encodingCharset = "UTF-8";

    /**
     * MD5加密（混淆）
     * 
     * @param str 需要加密的字符串
     * @return String 加密后的字符串
     */
    public static String encrypt(String str) {
        char str2[] = new char[16 * 2];
        try {
            char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
            MessageDigest mdt = MessageDigest.getInstance("MD5");
            mdt.update(str.getBytes());
            byte tmp[] = mdt.digest();
            int k = 0;
            for (int i = 0; i < 16; i++) {
                byte byte0 = tmp[i];
                str2[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str2[k++] = hexDigits[byte0 & 0xf];
            }
        } catch (Exception e) {
            throw new RuntimeException("MD5混淆出错。");
        }
        return new String(str2).toUpperCase();
    }

    /**
     * MD5加密（标准）转大写
     * 
     * @param str 需要加密的字符串
     * @return String 加密后的字符串
     */
    public static String md5UpperCase(String str) {
        return commonEncrypt(str, "MD5", true);
    }

    /**
     * MD5加密（标准）
     * 
     * @param str 需要加密的字符串
     * @return String 加密后的字符串
     */
    public static String md5(String str) {
        return commonEncrypt(str, "MD5", false);
    }

    /**
     * SHA1加密
     * 
     * @param str 需要加密的字符串
     * @return String 加密后的字符串
     */
    public static String sha1(String str) {
        return commonEncrypt(str, "SHA", true);
    }

    /**
     * 加密
     * 
     * @param str          加密字符串
     * @param instanceType 加密类型
     * @param UpperCase    是否转化为大写
     * @return String
     */
    private static String commonEncrypt(String str, String instanceType, boolean UpperCase) {
        str = str.trim();
        byte value[];
        try {
            value = str.getBytes(encodingCharset);
        } catch (UnsupportedEncodingException e) {
            value = str.getBytes();
        }
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance(instanceType);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
        if (UpperCase) {
            return toHex(md.digest(value)).toUpperCase();
        }
        return toHex(md.digest(value));
    }

    /**
     * 十六进制转换
     * 
     * @param input
     * @return String
     */
    private static String toHex(byte input[]) {
        if (input == null) {
            return null;
        }
        StringBuffer output = new StringBuffer(input.length * 2);
        for (int i = 0; i < input.length; i++) {
            int current = input[i] & 0xff;
            if (current < 16) {
                output.append("0");
            }
            output.append(Integer.toString(current, 16));
        }

        return output.toString();
    }

    /**
     * 16位数加密
     * @param content
     * @return
     */
    public static String encode(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(content.getBytes());
            return getEncode16(digest);
        } catch (NoSuchAlgorithmException e) {

        }
        return null;
    }

    /**
     * 16位加密
     *
     * @param digest
     * @return
     */
    private static String getEncode16(MessageDigest digest) {
        StringBuilder builder = new StringBuilder();
        for (byte b : digest.digest()) {
            builder.append(Integer.toHexString((b >> 4) & 0xf));
            builder.append(Integer.toHexString(b & 0xf));
        }
        // 16位加密，从第9位到25位
        // return builder.substring(8, 24).toString().toUpperCase();
        return builder.substring(8, 24).toString();
    }

    /**
     * md5混淆加密
     * @param source
     * @return
     */
    public static String getMD5(byte[] source) {
        String s = null;
        char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(source);
            byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数，
                                      // 用字节表示就是 16 个字节
            char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话，使用两个字符，
                                           // 所以表示成 16 进制需要 32 个字符
            int k = 0; // 表示转换结果中对应的字符位置
            for (int i = 0; i < 16; i++) { // 从第一个字节开始，对 MD5 的每一个字节
                                           // 转换成 16 进制字符的转换
                byte byte0 = tmp[i]; // 取第 i 个字节
                str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
                                                         // >>>
                                                         // 为逻辑右移，将符号位一起右移
                str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
            }
            s = new String(str); // 换后的结果转换为字符串

        } catch (Exception e) {
            e.printStackTrace();
        }
        return s;
    }

    /**
     * md5加密 截取中间16位
     * @param key 加密key
     * @return
     */
    public static String sign(String key) {
        String md5KeyValue = MD5Util.encrypt(key);
        return md5KeyValue.substring(8, md5KeyValue.length() - 8);
    }

    /**
     * 将除sign以外的请求参数（包括标准参数，除非有特别说明）按照参数名称的字典升序排列，然后按此顺序，将”参数名＝参数值”用”&”符号连接
     * @param params
     * @return
     */
    public static String packageSign(Map<String, String> params) {
        // 先将参数以其参数名的字典序升序进行排序
        TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
        // 去除参与的参数sign
        sortedParams.remove("sign");
        // 遍历排序后的字典，将所有参数按"key=value"格式拼接在一起
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Entry<String, String> param : sortedParams.entrySet()) {
            String value = param.getValue();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            if (first) {
                first = false;
            } else {
                sb.append("&");
            }
            sb.append(param.getKey()).append("=");
            sb.append(value);
        }
        return sb.toString();
    }

    /**
     * 二进制转十六进制字符串
     *
     * @param src
     * @return
     */
    public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString().toLowerCase();
    }

    public static byte[] getSHA1Digest(String data){
        byte[] bytes = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            bytes = md.digest(data.getBytes("UTF-8"));
        } catch (GeneralSecurityException gse) {
        }catch (Exception gse) {
        }
        return bytes;
    }

    /**
     * 解决方法
     * @param strKey
     * @param strIn
     * @return
     */
    public static String decrypt(String strKey, String strIn){
        String result=null;
        try {
            Decoder decoder = Base64.getDecoder();
            byte [] bytes = decoder.decode(strIn);
//            byte [] bytes = new BASE64Decoder().decodeBuffer(strIn);
            IvParameterSpec ivSpec = new IvParameterSpec("0102030405060708".getBytes());
            byte[] arrBTmp = strKey.getBytes();
            byte[] arrB = new byte[16]; // 创建一个空的16位字节数组（默认值为0）
            for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
                arrB[i] = arrBTmp[i];
            }
            SecretKeySpec skeySpec = new SecretKeySpec(arrB, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);               //与加密时不同MODE:Cipher.DECRYPT_MODE
            byte [] ret = cipher.doFinal(bytes);
            return new String(ret, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}